#include "BTreeNode.h"
using namespace std;

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{	
	pf.read(pid, buffer);
	return 0; }
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{ 
	pf.write(pid, buffer);
	return 0; }

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{ 
	return (strlen(buffer))/12; }

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid)
{ 
	int tempkey = -1;
	char keyChar[4];
	char pidChar[4];
	char sidChar[4];
	int kCount = getKeyCount();
	if(kCount == 85)
		return -1010;
	else
	{
		for(int x = 0; x < 1024 - 4 - 12; x = x + 12)
		{
			for(int a = 0; a < 4; a++)
			{
				keyChar[3 - a] = (key >> (a * 8)) & 0xFF;
				pidChar[3 - a] = (rid.pid >> (a * 8)) & 0xFF;
				sidChar[3 - a] = (rid.sid >> (a * 8)) & 0xFF;
			}
			if(kCount != 0)
			{
				if(kCount == 84)
				{
					for(int xx = 0; xx < 12; xx++)
					{
						if(xx < 4)
							buffer[1008 + xx] = keyChar[xx];
						else if (xx < 8)
							buffer[1008 + xx] = pidChar[xx];
						else
							buffer[1008 + xx] = sidChar[xx];
					}
					return 0;
				}
				else
				{
					tempkey = 0;
					tempkey = (tempkey << 8) + buffer[x+3];
					tempkey = (tempkey << 8) + buffer[x+2];
					tempkey = (tempkey << 8) + buffer[x+1];
					tempkey = (tempkey << 8) + buffer[x];
					
					if (key <= tempkey)
					{
						for(int y = 1024 - 4 - 12 - 1; y > x; y = y - 12)
						{
							for(int z = y; z > y - 12; z--)
							{
								buffer[z+12] = buffer[z];
							}
						}
						for(int xx = 0; xx < 12; xx++)
						{
							if(xx < 4)
								buffer[x + xx] = keyChar[xx];
							else if (xx < 8)
								buffer[x + xx] = pidChar[xx];
							else
								buffer[x + xx] = sidChar[xx];
						}
						return 0;
					}
				}
			}
			else
			{

				for(int xx = 0; xx < 12; xx++)
				{
					if(xx < 4)
						buffer[xx] = keyChar[xx];
					else if (xx < 8)
						buffer[xx] = pidChar[xx];
					else
						buffer[xx] = sidChar[xx];
				}
			}
		}
	}

}

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, 
                              BTLeafNode& sibling, int& siblingKey)
{ 
	int eid;
	locate(key, eid);
	int middleKey;
	RecordId middleRid;
	int splitID = ((getKeyCount()) / 2) + ((getKeyCount()) % 2);
	readEntry(splitID, middleKey, middleRid);
	int insertKey;
	RecordId insertRid;
	RecordId temp;
	PageId tpid = getNextNodePtr();


	if (key > middleKey)
	{
		
		for(int x = splitID + 1; x < (getKeyCount()); x++)
		{
			readEntry(x, insertKey, insertRid);
			sibling.insert(insertKey, insertRid);
		}
		sibling.insert(key, rid);
		sibling.readEntry(0, siblingKey, temp);
		for(int y = (splitID + 1) * 12; y < 1020; y++)
		{
			buffer[y] = '\0';
		}
		setNextNodePtr(temp.pid);
	}
	else
	{
		for(int x = splitID; x < (getKeyCount()); x++)
		{
			readEntry(x, insertKey, insertRid);
			sibling.insert(insertKey, insertRid);
		}
		for(int y = splitID * 12; y < 1020; y++)
		{
			buffer[y] = '\0';
		}
		insert(key, rid);
		sibling.readEntry(0, siblingKey, temp);
		setNextNodePtr(temp.pid);
	}
	sibling.setNextNodePtr(tpid);

	return 0; 

}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& eid)
{
int temp[85];
int tempkey = -1;
	for(int x = 0; x < 1024 - 4 - 12; x = x + 12)
			{
			tempkey = 0;
					tempkey = (tempkey << 8) + buffer[x+3];
					tempkey = (tempkey << 8) + buffer[x+2];
					tempkey = (tempkey << 8) + buffer[x+1];
					tempkey = (tempkey << 8) + buffer[x];
					int y = 0;
					y = x/12;
					temp[y] = tempkey;
					if(temp[y] >= searchKey)
					{
					eid = y;
					return 0;
					}
			}
 return -1012;
}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::readEntry(int eid, int& key, RecordId& rid)
{ 
	char keyChar[4];
	char pidChar[4];
	char sidChar[4];

	int location = 0 + 12 * eid;
	for(int a = 0; a < 4; a++)
	{
		keyChar[a] = buffer[location + a];
		pidChar[a] = buffer[location + a + 4];
		sidChar[a] = buffer[location + a + 8];
	}
	int tempkey = 0;
	int pid = 0;
	int sid = 0;
	
	for(int y = 3; y >= 0; y++)
	{
		tempkey = (tempkey << 8) + keyChar[y];
		pid = (pid << 8) + pidChar[y];
		sid = (sid << 8) + sidChar[y];
	}
	key = tempkey;
	rid.pid = pid;
	rid.sid = sid;
	return 0;
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{ 
int tempkey = -1;
for(int i = 1023; i > 1019; i--)
{
tempkey = 0;
tempkey = (tempkey << 8) + buffer[i];
}
 return tempkey;
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{
char pidChar[4];
for(int a = 0; a < 4; a++)
			{
				pidChar[3 - a] = (pid >> (a * 8)) & 0xFF;
			}
for (int i = 0; i < 4; i++)
{
	buffer[((getKeyCount()) * 8) + i] = pidChar[i];
}
 return 0;
}

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{ 
	pf.read(pid, buffer);
	return 0;
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{
	pf.write(pid, buffer);
	return 0;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{
int key = 0;
key = strlen(buffer)/8;
if ( key == 128)
	key = 127;
 return key; 
}


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(int key, PageId pid)
{
int tempkey = -1;
	char keyChar[4];
	char pidChar[4];
	int kCount = getKeyCount();
	if(kCount == 127)
		return -1010;
	else
	{
		for(int x = 0; x < 1024 - 4 - 8; x = x + 8)
		{
			for(int a = 0; a < 4; a++)
			{
				keyChar[3 - a] = (key >> (a * 8)) & 0xFF;
				pidChar[3 - a] = (pid >> (a * 8)) & 0xFF;
				
			}
			if(kCount != 0)
			{
				tempkey = 0;
				tempkey = (tempkey << 8) + buffer[x+3];
				tempkey = (tempkey << 8) + buffer[x+2];
				tempkey = (tempkey << 8) + buffer[x+1];
				tempkey = (tempkey << 8) + buffer[x];
				if (key <= tempkey)
				{
					for(int y = 1024 - 4 - 8 - 1; y > x; y = y - 4)
					{
						for(int z = y; z > y - 8; z--)
						{
							buffer[z+8] = buffer[z];
						}
					}
					for(int xx = 0; xx < 8; xx++)
					{
						if(xx < 4)
							buffer[x + xx] = pidChar[xx];
						else
							buffer[x + xx] = keyChar[xx];
					}
					return 0;
				}
			}
			else
			{
				for(int xx = 0; xx < 8; xx++)
				{
					if(xx < 4)
						buffer[xx] = pidChar[xx];
					else
						buffer[xx] = keyChar[xx];
				}
			}
		}
	}
}

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{
	int splitID = ((getKeyCount()) / 2) + ((getKeyCount()) % 2);
	int midPidLocation = splitID * 8;
	int lastPidLocation = ((getKeyCount()) * 8);
	PageId copyPid = 0;
	PageId lastPid = 0;
	for(int x = 3; x >= 0; x--)
	{
		copyPid = (copyPid << 8) + buffer[midPidLocation + x];
		lastPid = (lastPid << 8) + buffer[lastPidLocation + x];
	}
	char insertPid[4];
	char insertKey[4];
	for(int spot = midPidLocation; spot < ((getKeyCount()) * 8); spot++)
	{
		for(int y = 0; y < 4; y++)
		{
			insertPid[y] = buffer[spot + y];
			insertKey[y] = buffer[spot + 4 + y];
		}
		int tempPid = 0;
		int tempKey = 0;
		for(int z = 3; z >= 0; z--)
		{
			tempPid = (tempPid << 8) + insertPid[z];
			tempKey = (tempKey << 8) + insertKey[z];
		}
		if(spot == midPidLocation)
		{
			midKey = tempKey;
		}
		
		sibling.insert(tempKey, tempPid);
		for(int del = spot; del < spot + 8; del++)
		{
			buffer[del] = '\0';
		}
	}
	sibling.setLastPid(lastPid);
	setLastPid(copyPid);
	return 0;
}

RC BTNonLeafNode::setLastPid(PageId pid)
{
char pidChar[4];
for(int a = 0; a < 4; a++)
{
	pidChar[3 - a] = (pid >> (a * 8)) & 0xFF;
}
for (int i = 0; i < 4; i++)
{
	buffer[((getKeyCount()) * 8) + i] = pidChar[i];
}
 return 0;
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{ 
	int tempkey = -1;
	int temppid = -1;
	for(int x = 4; x < 1020; x = x + 8)
	{
		tempkey = 0;
		tempkey = (tempkey << 8) + buffer[x+3];
		tempkey = (tempkey << 8) + buffer[x+2];
		tempkey = (tempkey << 8) + buffer[x+1];
		tempkey = (tempkey << 8) + buffer[x];
		if(tempkey <= searchKey)
		{
			temppid = (temppid << 8) + buffer[x-1];
			temppid = (temppid << 8) + buffer[x-2];
			temppid = (temppid << 8) + buffer[x-3];
			temppid = (temppid << 8) + buffer[x-4];
			pid = temppid;
			return 0;
		}
	}
	temppid = (temppid << 8) + buffer[3];
	temppid = (temppid << 8) + buffer[2];
	temppid = (temppid << 8) + buffer[1];
	temppid = (temppid << 8) + buffer[0];
	pid = temppid;
	return 0;
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{ 
	char keyChar[4];
	char pid1Char[4];
	char pid2Char[4];
	for(int a = 0; a < 4; a++)
	{
		keyChar[3 - a] = (key >> (a * 8)) & 0xFF;
		pid1Char[3 - a] = (pid1 >> (a * 8)) & 0xFF;
		pid2Char[3 - a] = (pid2 >> (a * 8)) & 0xFF;
	}
	for(int i = 0; i < 12; i++)
	{
		if(i < 4)
		{
			buffer[i] = pid1Char[i];
		}
		else if (i < 8)
		{
			buffer[i] = keyChar[i-4];
		} 
		else
			buffer[i] = keyChar[i-8];
	}
	return 0; 
}
